﻿module FSChart.Helpers

let scaleF factor minv maxv =
    
    let minv = if minv = maxv then maxv - 1.0 else minv
    
    let order    = floor ( log10 (maxv - minv) )
    let delta    = 10.0 ** order
    let residual = (maxv - minv) / delta
    
    let step =
        factor *
        if   residual < 2.5 then 0.25 * delta
        elif residual < 5.0 then 0.50 * delta
        else delta
    
    let minSteps = floor(minv / step)
    let maxSteps = ceil (maxv / step)
    
    minSteps * step, maxSteps * step, step

let scaleD intraday target nDates (first : DateTime) (last : DateTime) =
    
    let ticksPerDay   = System.TimeSpan.TicksPerDay
    let ticksPerYear  = ticksPerDay * 365L
    let ticksPerMonth = ticksPerDay * 30L
    let ticksPerWeek  = ticksPerDay * 7L
    
    let everyNbYears n (_, d1 : DateTime) (_, d2 : DateTime) =
        d2.Year - d1.Year >= n, false
    
    let everyNbMonths n (_, d1 : DateTime) (_, d2 : DateTime) =
        12 * (d2.Year - d1.Year) + d2.Month - d1.Month >= n, false
    
    let everyNbWeeks n (_, d1 : DateTime) (_, d2 : DateTime) =
        ((d2.Date - d1.Date).Days + int d1.DayOfWeek) / 7 >= n, false
    
    let everyNbDays n (_, d1 : DateTime) (_, d2 : DateTime) =
        (d2.Date - d1.Date).Days >= n, false
    
    let everyNbPeriods n (k1, _) (k2, _) =
        k2 - k1 >= n, false
    
    let everyNbBars n (k1, d1 : DateTime) (k2, d2 : DateTime) =
        if d2.Date = d1.Date
        then k2 - k1 >= n, false
        else true, 3*(k2 - k1) < 2*n
    
    let n = max 1 (min target (nDates - 1))
    let k = (nDates - 1) / n
    
    let delta  = last.Subtract(first).Ticks / int64 n
    let deltaY = float delta / float ticksPerYear  |> round |> int
    let deltaM = float delta / float ticksPerMonth |> round |> int
    let deltaW = float delta / float ticksPerWeek  |> round |> int
    let deltaD = float delta / float ticksPerDay   |> round |> int
    
    if   deltaY >= 1 then everyNbYears   deltaY
    elif deltaM >= 1 then
        match deltaM with
        | 1 | 2 | 3 ->    everyNbMonths  deltaM
        | 4 | 5 | 6 ->    everyNbMonths  6
        | _         ->    everyNbYears   1
    elif deltaW >= 1 then
        match deltaW with
        | 1 | 2     ->    everyNbWeeks   deltaW
        | _         ->    everyNbMonths  1
    elif not intraday then
        match deltaD with
        | 1 | 2 | 3 ->    everyNbPeriods deltaD
        | _         ->    everyNbWeeks   1
    else
        match deltaD with
        | 0         ->    everyNbBars    k
        | 1 | 2     ->    everyNbDays    deltaD
        | _         ->    everyNbWeeks   1

